import tkinter as tk
import tkinter.ttk as ttk
import tkinter.scrolledtext as scrolledtext
import tkinter.filedialog as filedialog
import tkinter.messagebox as messagebox
import re
import json
import os
import sys
from PIL import Image
from PIL import ImageTk
from collections import Counter
from datetime import datetime

PIL_AVAILABLE = True
MATPLOTLIB_AVAILABLE = True

# 确保从正确的模块导入所需函数
from src.storage import save_characters, load_characters
from src.core import create_character, delete_character, edit_character

# GUI组件函数
def create_splash_screen():
    """创建启动屏幕（欢迎页面）"""
    # 创建启动屏幕窗口
    splash = tk.Toplevel()
    splash.overrideredirect(True)  # 去除窗口边框
    splash.configure(bg="#1a1a2e")  # 使用深色主题背景
    
    # 计算屏幕居中位置
    width = 600
    height = 400
    x = (splash.winfo_screenwidth() // 2) - (width // 2)
    y = (splash.winfo_screenheight() // 2) - (height // 2)
    splash.geometry(f"{width}x{height}+{x}+{y}")
    
    # 创建标题标签
    title_label = tk.Label(
        splash, 
        text="游戏角色收藏管理系统", 
        font=("微软雅黑", 24, "bold"), 
        bg="#1a1a2e", 
        fg="#e94560"
    )
    title_label.pack(pady=(80, 20))
    
    # 创建副标题标签
    subtitle_label = tk.Label(
        splash, 
        text="您的专属游戏角色收藏库", 
        font=("微软雅黑", 16), 
        bg="#1a1a2e", 
        fg="#ffffff"
    )
    subtitle_label.pack(pady=10)
    
    # 创建装饰性分隔线
    separator = ttk.Separator(splash, orient="horizontal")
    separator.pack(fill="x", padx=50, pady=20)
    
    # 创建加载指示器
    loading_var = tk.StringVar(value="正在加载...")
    loading_label = tk.Label(
        splash, 
        textvariable=loading_var, 
        font=("微软雅黑", 12), 
        bg="#1a1a2e", 
        fg="#00ffcc"
    )
    loading_label.pack(pady=20)
    
    # 创建底部版权信息
    footer_label = tk.Label(
        splash, 
        text="© 2024 游戏角色收藏管理系统", 
        font=("微软雅黑", 10), 
        bg="#1a1a2e", 
        fg="#888888"
    )
    footer_label.pack(side="bottom", pady=20)
    
    # 添加加载动画效果
    splash.after_id = None
    
    def animate_loading():
        current_text = loading_var.get()
        dots = current_text.count('.')
        if dots >= 3:
            loading_var.set("正在加载")
        else:
            loading_var.set(current_text + ".")
        splash.after_id = splash.after(500, animate_loading)
    
    animate_loading()
    
    return splash

def create_main_window():
    """创建主窗口"""
    window = tk.Tk()
    # 设置纯色背景（浅蓝色）
    window.configure(bg="#f0f8ff")
    # 创建自定义ttk样式
    setup_custom_styles(window)
    
    window.title("游戏角色收藏管理系统")
    window.geometry("800x600")
    window.minsize(700, 500)
    
    return window

def create_tree_view(parent):
    """创建角色列表表格"""
    columns = ("id", "game_name", "char_name", "char_type")
    tree = ttk.Treeview(parent, columns=columns, show="headings", style="Rounded.Treeview")
    
    # 设置列宽和标题
    tree.heading("id", text="角色ID")
    tree.heading("game_name", text="游戏名(game name)")
    tree.heading("char_name", text="角色名(character name)")
    tree.heading("char_type", text="角色类型(character type)")
    
    tree.column("id", width=80, anchor="center")
    tree.column("game_name", width=200, anchor="center")
    tree.column("char_name", width=200, anchor="center")
    tree.column("char_type", width=150, anchor="center")
    
    # 添加滚动条
    scrollbar = ttk.Scrollbar(parent, orient="vertical", command=tree.yview)
    tree.configure(yscrollcommand=scrollbar.set)
    scrollbar.pack(side="right", fill="y")
    tree.pack(side="left", fill="both", expand=True, padx=5, pady=5)
    
    return tree

def setup_custom_styles(window):
    """设置自定义样式，包括圆角和背景"""
    style = ttk.Style(window)
    
    # 标准ttk中创建自定义样式（可能不会显示圆角，但可以设置其他样式）
    try:
        # 尝试设置圆角相关属性
        style.configure("Rounded.TButton", 
                       padding=(10, 5),
                       font=("SimHei", 10, "normal"))
        
        style.configure("Rounded.TFrame", 
                       padding=10,
                       relief="flat",
                       background="#f0f8ff")
        
        style.configure("Rounded.Treeview",
                       font=("SimHei", 10),
                       rowheight=25)
    except Exception as e:
        print(f"设置样式时出错: {e}")

def create_search_frame(parent, search_callback, history_callback, page_callback):
    """创建搜索框架"""
    frame = ttk.Frame(parent, style="Rounded.TFrame", padding="10")
    frame.pack(fill="x", padx=10, pady=5)
    
    # 搜索输入框
    ttk.Label(frame, text="搜索关键词:").pack(side="left", padx=5)
    search_var = tk.StringVar()
    search_entry = ttk.Entry(frame, textvariable=search_var, width=30)
    search_entry.pack(side="left", padx=5)
    search_entry.bind("<Return>", lambda event: search_callback(search_var.get()))
    
    # 添加自动完成下拉菜单
    autocomplete_listbox = tk.Listbox(frame, width=30, height=5)
    autocomplete_listbox.place_forget()  # 初始隐藏
    
    # 自动完成函数
    def on_key_release(event):
        current = search_var.get()
        if not current:
            autocomplete_listbox.place_forget()
            return
        
        # 获取所有可能的关键词
        keywords = set()
        if 'app_state' in globals() and 'characters' in app_state:
            for char in app_state['characters']:
                # 从游戏名、角色名、角色类型中提取关键词
                for field in ['game_name', 'char_name', 'char_type']:
                    if char[field] and current.lower() in char[field].lower():
                        keywords.add(char[field])
        
        # 更新下拉菜单
        autocomplete_listbox.delete(0, tk.END)
        for keyword in sorted(keywords)[:10]:  # 最多显示10个建议
            autocomplete_listbox.insert(tk.END, keyword)
        
        # 显示或隐藏下拉菜单
        if autocomplete_listbox.size() > 0:
            x, y, width, height = search_entry.winfo_x(), search_entry.winfo_y(), search_entry.winfo_width(), search_entry.winfo_height()
            autocomplete_listbox.place(x=x, y=y+height, width=width)
        else:
            autocomplete_listbox.place_forget()
    
    # 选择自动完成项
    def on_select(event):
        if autocomplete_listbox.curselection():
            index = autocomplete_listbox.curselection()[0]
            selected = autocomplete_listbox.get(index)
            search_var.set(selected)
            autocomplete_listbox.place_forget()
            # 自动执行搜索
            search_callback(selected)
    
    # 绑定事件
    search_entry.bind("<KeyRelease>", on_key_release)
    autocomplete_listbox.bind("<<ListboxSelect>>", on_select)
    
    # 点击其他地方隐藏下拉菜单
    def on_click(event):
        if event.widget != search_entry and event.widget != autocomplete_listbox:
            autocomplete_listbox.place_forget()
    
    parent.bind("<Button-1>", on_click)
    
    # 保存引用供外部使用
    frame.autocomplete_listbox = autocomplete_listbox
    
    # 搜索按钮
    ttk.Button(frame, text="搜索(serch)", command=lambda: search_callback(search_var.get()), style="Rounded.TButton").pack(side="left", padx=5)
    ttk.Button(frame, text="重置(Reset)", command=lambda: (search_var.set(""), search_callback("")), style="Rounded.TButton").pack(side="left", padx=5)
    
    # 搜索历史下拉框
    ttk.Label(frame, text="历史记录(History record):").pack(side="left", padx=5)
    history_var = tk.StringVar()
    history_combo = ttk.Combobox(frame, textvariable=history_var, width=20, state="readonly")
    history_combo.pack(side="left", padx=5)
    history_combo.bind("<<ComboboxSelected>>", lambda event: history_callback(history_var.get()))
    
    # 分页控件
    page_frame = ttk.Frame(frame)
    page_frame.pack(side="right", padx=5)
    
    ttk.Label(page_frame, text="每页显示(Display per page):").pack(side="left", padx=2)
    page_size_var = tk.StringVar(value="10")
    page_size_combo = ttk.Combobox(page_frame, textvariable=page_size_var, values=["5", "10", "20", "50"], width=5, state="readonly")
    page_size_combo.pack(side="left", padx=2)
    page_size_combo.bind("<<ComboboxSelected>>", lambda event: page_callback(int(page_size_var.get()), 1))
    
    # 保存引用供外部使用
    frame.search_var = search_var
    frame.history_combo = history_combo
    frame.page_size_var = page_size_var
    frame.page_callback = page_callback
    
    return frame

def create_button_frame(parent, callbacks):
    """创建按钮框架"""
    frame = ttk.Frame(parent, style="Rounded.TFrame", padding="10")
    frame.pack(fill="x", padx=10, pady=5)
    
    # 创建圆角按钮
    ttk.Button(frame, text="添加角色(Add a role)", command=callbacks['add_char'], style="Rounded.TButton").pack(side="left", padx=5)
    ttk.Button(frame, text="编辑角色(edit a role)", command=callbacks['edit_char'], style="Rounded.TButton").pack(side="left", padx=5)
    ttk.Button(frame, text="查看详情(check detials)", command=callbacks['view_details'], style="Rounded.TButton").pack(side="left", padx=5)
    ttk.Button(frame, text="删除角色(delete role)", command=callbacks['delete_char'], style="Rounded.TButton").pack(side="left", padx=5)
    ttk.Button(frame, text="刷新列表(reset list)", command=callbacks['refresh_list'], style="Rounded.TButton").pack(side="left", padx=5)
    ttk.Button(frame, text="保存数据(save data)", command=callbacks['save_data'], style="Rounded.TButton").pack(side="left", padx=5)
    
    # 添加统计功能按钮
    ttk.Button(frame, text="数据统计(Data statistics)", command=callbacks['show_statistics'], style="Rounded.TButton").pack(side="right", padx=5)
    ttk.Button(frame, text="退出系统(Exit the system)", command=callbacks['exit_app'], style="Rounded.TButton").pack(side="right", padx=5)
    
    return frame

def create_pagination_frame(parent, page_callback, total_pages_var):
    """创建分页控制框架"""
    frame = ttk.Frame(parent, style="Rounded.TFrame", padding="5")
    frame.pack(fill="x", padx=10, pady=5)
    
    current_page_var = tk.StringVar(value="1")
    
    def go_to_page(page_num):
        current_page_var.set(str(page_num))
        page_callback(int(page_num))
    
    ttk.Button(frame, text="首页(Homepage)", command=lambda: go_to_page(1), style="Rounded.TButton").pack(side="left", padx=2)
    ttk.Button(frame, text="上一页(Previous page)", command=lambda: go_to_page(max(1, int(current_page_var.get()) - 1)), style="Rounded.TButton").pack(side="left", padx=2)
    
    # 页码显示
    page_label = ttk.Label(frame, textvariable=total_pages_var)
    page_label.pack(side="left", padx=10)
    
    # 页码输入
    ttk.Label(frame, text="页码:(page)").pack(side="left", padx=2)
    page_entry = ttk.Entry(frame, textvariable=current_page_var, width=5)
    page_entry.pack(side="left", padx=2)
    page_entry.bind("<Return>", lambda event: go_to_page(min(int(total_pages_var.get().split("/")[1]), max(1, int(current_page_var.get())))))
    
    ttk.Button(frame, text="下一页(next page)", command=lambda: go_to_page(min(int(total_pages_var.get().split("/")[1]), int(current_page_var.get()) + 1)), style="Rounded.TButton").pack(side="left", padx=2)
    ttk.Button(frame, text="末页(last page)", command=lambda: go_to_page(int(total_pages_var.get().split("/")[1])), style="Rounded.TButton").pack(side="left", padx=2)
    
    # 保存引用供外部使用
    frame.current_page_var = current_page_var
    
    return frame

def create_status_bar(parent):
    """创建状态栏"""
    status_var = tk.StringVar()
    status_var.set("就绪(ready)")
    
    status_bar = ttk.Label(parent, textvariable=status_var, relief="sunken", anchor="w")
    status_bar.pack(side="bottom", fill="x")
    
    return status_var

def create_add_character_dialog(parent, on_success):
    """创建添加角色对话框，支持图片选择"""
    # 创建对话框
    dialog = tk.Toplevel(parent)
    dialog.title("添加新角色(add a new role)")
    dialog.geometry("500x300")
    dialog.transient(parent)
    dialog.grab_set()
    
    # 应用样式到对话框
    dialog.configure(bg="#f0f8ff")
    
    selected_image_path = tk.StringVar()
    
    def on_submit():
        game_name = game_entry.get().strip()
        char_name = name_entry.get().strip()
        char_type = type_entry.get().strip()
        
        if not all([game_name, char_name, char_type]):
            messagebox.showwarning("输入错误", "请填写所有字段")
            return
        
        image_path = selected_image_path.get().strip() if selected_image_path.get() else None
        dialog.destroy()
        on_success(game_name, char_name, char_type, image_path)
    
    def select_image():
        # 只有在PIL可用时才提供图片选择功能
        if not PIL_AVAILABLE:
            messagebox.showinfo("提示", "图片功能不可用，请先安装PIL库")
            return
            
        file_types = [
            ("图片文件", "*.png;*.jpg;*.jpeg;*.gif;*.bmp"),
            ("所有文件", "*.*")
        ]
        file_path = filedialog.askopenfilename(
            title="选择角色图片(Select a character image)",
            filetypes=file_types
        )
        if file_path:
            selected_image_path.set(file_path)
            image_label.config(text=f"已选择(Selected): {os.path.basename(file_path)}")
    
    # 创建表单
    frame = ttk.Frame(dialog, style="Rounded.TFrame", padding="20")
    frame.pack(fill="both", expand=True)
    
    ttk.Label(frame, text="游戏名(game name):").grid(row=0, column=0, sticky="w", pady=10)
    game_entry = ttk.Entry(frame, width=30)
    game_entry.grid(row=0, column=1, sticky="ew", pady=10)
    
    ttk.Label(frame, text="角色名(role name):").grid(row=1, column=0, sticky="w", pady=10)
    name_entry = ttk.Entry(frame, width=30)
    name_entry.grid(row=1, column=1, sticky="ew", pady=10)
    
    ttk.Label(frame, text="角色类型(role type):").grid(row=2, column=0, sticky="w", pady=10)
    type_entry = ttk.Entry(frame, width=30)
    type_entry.grid(row=2, column=1, sticky="ew", pady=10)
    
    # 只有在PIL可用时才显示图片选择部分
    if PIL_AVAILABLE:
        ttk.Label(frame, text="角色图片(role picture):").grid(row=3, column=0, sticky="w", pady=10)
        image_frame = ttk.Frame(frame)
        image_frame.grid(row=3, column=1, sticky="ew", pady=10)
        
        ttk.Button(image_frame, text="选择图片(shoose picture)", command=select_image, style="Rounded.TButton").pack(side="left", padx=5)
        image_label = ttk.Label(image_frame, text="未选择图片(No picture selected)", wraplength=300)
        image_label.pack(side="left", fill="x", expand=True)
        
        btn_row = 4
    else:
        # 不显示图片选择部分
        btn_row = 3
    
    # 使列宽适应窗口大小
    frame.columnconfigure(1, weight=1)
    
    # 底部按钮
    btn_frame = ttk.Frame(frame)
    btn_frame.grid(row=btn_row, column=0, columnspan=2, pady=20, sticky="e")
    
    ttk.Button(btn_frame, text="取消(cancel)", command=dialog.destroy, style="Rounded.TButton").pack(side="right", padx=5)
    ttk.Button(btn_frame, text="添加(add)", command=on_submit, style="Rounded.TButton").pack(side="right", padx=5)

def create_edit_character_dialog(parent, character, on_success):
    """创建编辑角色对话框，支持图片选择和修改"""
    # 创建对话框
    dialog = tk.Toplevel(parent)
    dialog.title(f"编辑角色(edit role) - {character['char_name']}")
    dialog.geometry("500x300")
    dialog.transient(parent)
    dialog.grab_set()
    
    # 应用样式到对话框
    dialog.configure(bg="#f0f8ff")
    
    # 存储当前图片路径
    current_image_path = character.get('image_path', '')
    selected_image_path = tk.StringVar(value=current_image_path)
    
    def on_submit():
        game_name = game_entry.get().strip()
        char_name = name_entry.get().strip()
        char_type = type_entry.get().strip()
        
        if not all([game_name, char_name, char_type]):
            messagebox.showwarning("输入错误(enter error)", "请填写所有字段(Please fill in all fields)")
            return
        
        # 获取图片路径，如果没有变化则使用None表示不修改
        image_path = selected_image_path.get().strip()
        image_path = image_path if image_path != current_image_path else None
        
        dialog.destroy()
        on_success(game_name, char_name, char_type, image_path)
    
    def select_image():
        # 只有在PIL可用时才提供图片选择功能
        if not PIL_AVAILABLE:
            messagebox.showinfo("提示", "图片功能不可用，请先安装PIL库")
            return
            
        file_types = [
            ("图片文件", "*.png;*.jpg;*.jpeg;*.gif;*.bmp"),
            ("所有文件", "*.*")
        ]
        file_path = filedialog.askopenfilename(
            title="选择角色图片(Select a character image)",
            filetypes=file_types
        )
        if file_path:
            selected_image_path.set(file_path)
            image_label.config(text=f"已选择(selected): {os.path.basename(file_path)}")
    
    def clear_image():
        selected_image_path.set('')
        image_label.config(text="未选择图片(No picture selected)")
    
    # 创建表单
    frame = ttk.Frame(dialog, style="Rounded.TFrame", padding="20")
    frame.pack(fill="both", expand=True)
    
    # 显示角色ID（不可编辑）
    ttk.Label(frame, text="角色ID:").grid(row=0, column=0, sticky="w", pady=5)
    ttk.Label(frame, text=character['id']).grid(row=0, column=1, sticky="w", pady=5)
    
    ttk.Label(frame, text="游戏名(game name):").grid(row=1, column=0, sticky="w", pady=10)
    game_entry = ttk.Entry(frame, width=30)
    game_entry.insert(0, character['game_name'])
    game_entry.grid(row=1, column=1, sticky="ew", pady=10)
    
    ttk.Label(frame, text="角色名(role name):").grid(row=2, column=0, sticky="w", pady=10)
    name_entry = ttk.Entry(frame, width=30)
    name_entry.insert(0, character['char_name'])
    name_entry.grid(row=2, column=1, sticky="ew", pady=10)
    
    ttk.Label(frame, text="角色类型(role type):").grid(row=3, column=0, sticky="w", pady=10)
    type_entry = ttk.Entry(frame, width=30)
    type_entry.insert(0, character['char_type'])
    type_entry.grid(row=3, column=1, sticky="ew", pady=10)
    
    # 只有在PIL可用时才显示图片选择部分
    if PIL_AVAILABLE:
        ttk.Label(frame, text="角色图片(role picture):").grid(row=4, column=0, sticky="w", pady=10)
        image_frame = ttk.Frame(frame)
        image_frame.grid(row=4, column=1, sticky="ew", pady=10)
        
        ttk.Button(image_frame, text="选择图片(choose picture)", command=select_image, style="Rounded.TButton").pack(side="left", padx=5)
        if current_image_path:
            image_label = ttk.Label(image_frame, text=f"已选择(Selected): {os.path.basename(current_image_path)}", wraplength=250)
            ttk.Button(image_frame, text="清除(Clear)", command=clear_image, style="Rounded.TButton").pack(side="left", padx=5)
        else:
            image_label = ttk.Label(image_frame, text="未选择图片(No picture selected)", wraplength=300)
        image_label.pack(side="left", fill="x", expand=True)
        
        btn_row = 5
    else:
        # 不显示图片选择部分
        btn_row = 4
    
    # 使列宽适应窗口大小
    frame.columnconfigure(1, weight=1)
    
    # 底部按钮
    btn_frame = ttk.Frame(frame)
    btn_frame.grid(row=btn_row, column=0, columnspan=2, pady=20, sticky="e")
    
    ttk.Button(btn_frame, text="取消(cancel)", command=dialog.destroy, style="Rounded.TButton").pack(side="right", padx=5)
    ttk.Button(btn_frame, text="保存(save)", command=on_submit, style="Rounded.TButton").pack(side="right", padx=5)

# 主应用函数
import re

def highlight_text(text, keyword):
    """高亮显示关键词"""
    print(f"[DEBUG] 高亮文本: '{text}' 关键词: '{keyword}'")
    if not keyword:
        return text
    
    try:
        # 使用正则表达式进行大小写不敏感的匹配
        pattern = re.compile(re.escape(keyword), re.IGNORECASE)
        
        # 使用符号夹住关键词来实现高亮显示，符合用户期望的样式
        highlighted_text = pattern.sub(lambda m: f"[{m.group(0)}]", text)
        print(f"[DEBUG] 高亮结果: '{highlighted_text}'")
        return highlighted_text
    except Exception as e:
        print(f"[ERROR] 高亮文本时出错: {e}")
        return text  # 出错时返回原始文本

def show_statistics_dialog(parent, characters):
    """显示统计信息对话框"""
    dialog = tk.Toplevel(parent)
    dialog.title("数据统计(Data statistics)")
    dialog.geometry("1000x700")
    dialog.transient(parent)
    dialog.grab_set()
    
    # 应用样式到对话框
    dialog.configure(bg="#f0f8ff")
    
    # 创建标签页
    notebook = ttk.Notebook(dialog)
    notebook.pack(fill="both", expand=True, padx=10, pady=10)
    
    # 基础统计标签页
    basic_tab = ttk.Frame(notebook)
    notebook.add(basic_tab, text="基础统计(Basic Statistics)")
    
    # 统计信息
    stats_frame = ttk.Frame(basic_tab, style="Rounded.TFrame", padding="20")
    stats_frame.pack(fill="x", padx=10, pady=10)
    
    total_chars = len(characters)
    ttk.Label(stats_frame, text=f"总角色数(Total number of roles): {total_chars}", font=("SimHei", 12, "bold")).pack(anchor="w", pady=5)
    
    # 按游戏统计
    game_counts = Counter(char['game_name'] for char in characters)
    ttk.Label(stats_frame, text="\n各游戏角色数量(umber of each game characte):", font=("SimHei", 11, "bold")).pack(anchor="w", pady=5)
    
    game_listbox = tk.Listbox(stats_frame, width=50, height=8)
    game_listbox.pack(fill="x", pady=5)
    for game, count in game_counts.most_common():
        game_listbox.insert("end", f"{game}: {count} 个角色(character)")
    
    # 按角色类型统计
    type_counts = Counter(char['char_type'] for char in characters)
    ttk.Label(stats_frame, text="\n各类型角色数量(Number of roles of each type):", font=("SimHei", 11, "bold")).pack(anchor="w", pady=5)
    
    type_listbox = tk.Listbox(stats_frame, width=50, height=8)
    type_listbox.pack(fill="x", pady=5)
    for char_type, count in type_counts.most_common():
        type_listbox.insert("end", f"{char_type}: {count} 个角色(character)")
    
    # 如果matplotlib可用，添加图表标签页
    if MATPLOTLIB_AVAILABLE:
        chart_tab = ttk.Frame(notebook)
        notebook.add(chart_tab, text="统计图表(Statistical charts)")
        
        # 创建图表控件
        create_charts_tab(chart_tab, characters)
    
    # 导出按钮
    btn_frame = ttk.Frame(basic_tab)
    btn_frame.pack(pady=20)
    
    def export_statistics():
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"statistics_{timestamp}.json"
            file_path = os.path.join(os.getcwd(), filename)
            
            stats_data = {
                "export_time": datetime.now().isoformat(),
                "total_characters": total_chars,
                "games_statistics": dict(game_counts),
                "types_statistics": dict(type_counts),
                "characters_sample": characters[:10] if len(characters) > 10 else characters
            }
            
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(stats_data, f, ensure_ascii=False, indent=2)
            
            messagebox.showinfo("成功(successful)", f"统计数据已导出到(Statistical data has been exported to): {file_path}")
        except Exception as e:
            messagebox.showerror("错误(error)", f"导出失败(Export failed): {str(e)}")
    
    ttk.Button(btn_frame, text="导出统计数据(Export statistical data)", command=export_statistics, style="Rounded.TButton").pack()

def create_charts_tab(parent, characters):
    """创建图表标签页"""
    # 创建筛选控件
    filter_frame = ttk.Frame(parent, style="Rounded.TFrame", padding="10")
    filter_frame.pack(fill="x", padx=10, pady=10)
    
    ttk.Label(filter_frame, text="图表类型:").pack(side="left", padx=5)
    chart_type_var = tk.StringVar(value="柱状图")
    chart_type_combo = ttk.Combobox(filter_frame, textvariable=chart_type_var, values=["柱状图", "折线图", "饼图"], state="readonly")
    chart_type_combo.pack(side="left", padx=5)
    
    ttk.Label(filter_frame, text="统计维度:").pack(side="left", padx=5)
    dimension_var = tk.StringVar(value="按游戏")
    dimension_combo = ttk.Combobox(filter_frame, textvariable=dimension_var, values=["按游戏", "按类型"], state="readonly")
    dimension_combo.pack(side="left", padx=5)
    
    def update_chart():
        # 清除现有图表
        for widget in chart_frame.winfo_children():
            widget.destroy()
        
        # 创建图表
        fig, ax = plt.subplots(figsize=(8, 5), dpi=100)
        
        # 根据选择的维度获取数据
        if dimension_var.get() == "按游戏":
            data = Counter(char['game_name'] for char in characters)
            title = "各游戏角色数量统计"
        else:
            data = Counter(char['char_type'] for char in characters)
            title = "各类型角色数量统计"
        
        # 根据选择的图表类型绘制
        labels = list(data.keys())
        values = list(data.values())
        
        if chart_type_var.get() == "柱状图":
            bars = ax.bar(labels, values, color='skyblue')
            ax.bar_label(bars, labels=values, padding=3)
        elif chart_type_var.get() == "折线图":
            ax.plot(labels, values, marker='o', color='skyblue', linewidth=2)
            for x, y in zip(range(len(labels)), values):
                ax.text(x, y+0.1, str(y), ha='center')
        else:  # 饼图
            ax.pie(values, labels=labels, autopct='%1.1f%%', startangle=90, colors=plt.cm.Pastel1.colors)
            ax.axis('equal')
        
        ax.set_title(title, fontsize=14)
        plt.xticks(rotation=45, ha='right')
        plt.tight_layout()
        
        # 将图表嵌入到Tkinter窗口
        canvas = FigureCanvasTkAgg(fig, master=chart_frame)
        canvas.draw()
        canvas.get_tk_widget().pack(fill="both", expand=True)
    
    ttk.Button(filter_frame, text="更新图表", command=update_chart, style="Rounded.TButton").pack(side="left", padx=5)
    
    # 创建图表显示区域
    chart_frame = ttk.Frame(parent)
    chart_frame.pack(fill="both", expand=True, padx=10, pady=10)
    
    # 初始显示图表
    update_chart()

def run_gui_app():
    """运行GUI应用程序"""
    import time
    
    # 声明全局变量
    global search_frame, tree, app_state, handle_search
    # 确保这些变量在模块级别可用
    globals()['app_state'] = None
    globals()['handle_search'] = None
    
    # 首先创建一个隐藏的根窗口
    root = tk.Tk()
    root.withdraw()  # 隐藏根窗口
    
    # 显示启动屏幕
    splash = create_splash_screen()
    
    # 状态管理（使用闭包而非全局变量）
    app_state = {
        'characters': [],
        'search_results': [],
        'search_history': [],
        'current_page': 1,
        'page_size': 10,
        'is_searching': False
    }
    
    # 搜索相关函数
    def handle_search(keyword):
        # 确保此函数和app_state在全局作用域可用
        globals()['handle_search'] = handle_search
        globals()['app_state'] = app_state
        print(f"[DEBUG] 开始搜索: '{keyword}'")
        
        # 确保tree变量存在且有效（同时检查局部和全局作用域）
        tree_widget = None
        if 'tree' in locals():
            tree_widget = locals()['tree']
        elif 'tree' in globals():
            tree_widget = globals()['tree']
            print("[DEBUG] 从全局作用域获取tree变量")
        
        if tree_widget is None:
            print("[ERROR] tree变量不存在或无效")
            return
        
        print(f"[DEBUG] Tree组件: {tree_widget}")
        print(f"[DEBUG] 当前角色总数: {len(app_state['characters'])}")
        
        app_state['current_page'] = 1
        if 'pagination_frame' in locals() and pagination_frame:
            pagination_frame.current_page_var.set("1")
        
        if not keyword.strip():
            print("[DEBUG] 空搜索请求")
            app_state['is_searching'] = False
            app_state['search_results'] = []
            # 直接更新数据，然后调用display_paged_results
            display_paged_results(tree_widget)
            if 'total_pages_var' in locals():
                total_pages = (len(app_state['characters']) + app_state['page_size'] - 1) // app_state['page_size']
                total_pages_var.set(f"{app_state['current_page']}/{total_pages if total_pages > 0 else 1}")
            if 'status_var' in locals():
                status_var.set(f"显示全部角色，共 {len(app_state['characters'])} 个")
            print("[DEBUG] 空搜索完成")
            return
        
        # 执行搜索
        keyword = keyword.strip().lower()
        print(f"[DEBUG] 执行关键词搜索: '{keyword}'")
        results = []
        
        for char in app_state['characters']:
            # 按角色名称、游戏名称、角色类型和ID进行大小写不敏感的搜索
            match_found = False
            
            if keyword in char['game_name'].lower():
                print(f"[DEBUG] 匹配游戏名称: '{char['game_name']}'")
                match_found = True
            elif keyword in char['char_name'].lower():
                print(f"[DEBUG] 匹配角色名称: '{char['char_name']}'")
                match_found = True
            elif keyword in char['char_type'].lower():
                print(f"[DEBUG] 匹配角色类型: '{char['char_type']}'")
                match_found = True
            elif keyword in char['id'].lower():
                print(f"[DEBUG] 匹配角色ID: '{char['id']}'")
                match_found = True
            
            if match_found:
                results.append(char)
                print(f"[DEBUG] 添加匹配角色: {char['char_name']} (ID: {char['id']})")
        
        # 更新搜索结果和历史
        app_state['is_searching'] = True
        app_state['search_results'] = results
        print(f"[DEBUG] 搜索结果数量: {len(results)}")
        
        # 添加到搜索历史（避免重复）
        if keyword not in app_state['search_history']:
            print(f"[DEBUG] 添加到搜索历史: '{keyword}'")
            app_state['search_history'].insert(0, keyword)
            if len(app_state['search_history']) > 10:  # 保留最近10条
                app_state['search_history'] = app_state['search_history'][:10]
            
            # 更新历史下拉框
            if search_frame:  # 确保search_frame存在
                search_frame.history_combo['values'] = app_state['search_history']
                print(f"[DEBUG] 更新历史下拉框，历史记录数: {len(app_state['search_history'])}")
        
        # 显示搜索结果（带分页）
        print("[DEBUG] 调用display_paged_results显示结果")
        display_paged_results(tree_widget)
        
        # 更新状态
        if 'status_var' in locals():
            status_var.set(f"搜索结果: 找到 {len(results)} 个匹配角色")
        print(f"[DEBUG] 搜索完成: 找到 {len(results)} 个匹配结果")
    
    def handle_history_select(history):
        if 'search_frame' in locals() and search_frame:
            search_frame.search_var.set(history)
            handle_search(history)
    
    def handle_page_change(page_size, page):
        app_state['page_size'] = page_size
        app_state['current_page'] = page
        
        # 更新分页控件
        if app_state['is_searching']:
            total_pages = (len(app_state['search_results']) + page_size - 1) // page_size
        else:
            total_pages = (len(app_state['characters']) + page_size - 1) // page_size
        
        if 'total_pages_var' in locals():
            total_pages_var.set(f"{page}/{total_pages if total_pages > 0 else 1}")
        if 'pagination_frame' in locals() and pagination_frame:
            pagination_frame.current_page_var.set(str(page))
        
        # 显示对应页的数据
        if 'tree' in locals():
            display_paged_results(tree)
    
    def display_paged_results(tree_widget):
        # 验证参数
        if not tree_widget:
            return
        
        try:
            # 清空现有数据
            for item in tree_widget.get_children():
                tree_widget.delete(item)
            
            # 确定要显示的数据
            data = app_state['search_results'] if app_state['is_searching'] else app_state['characters']
            
            # 计算分页范围
            start_idx = (app_state['current_page'] - 1) * app_state['page_size']
            end_idx = start_idx + app_state['page_size']
            paged_data = data[start_idx:end_idx]
        except Exception:
            # 如果发生错误（如tree_widget已被销毁），直接返回
            return
        
        # 获取当前搜索关键词用于高亮
        keyword = ""
        if 'search_frame' in locals() and search_frame:
            keyword = search_frame.search_var.get().strip()
        
        # 添加数据到treeview
        for char in paged_data:
            # 如果有搜索关键词，高亮显示
            if keyword:
                values = (
                    highlight_text(char['id'], keyword),
                    highlight_text(char['game_name'], keyword),
                    highlight_text(char['char_name'], keyword),
                    highlight_text(char['char_type'], keyword)
                )
            else:
                values = (char['id'], char['game_name'], char['char_name'], char['char_type'])
            
            tree_widget.insert('', 'end', values=values)
    

    
    def handle_view_details():
        if 'tree' not in locals():
            return
        
        selected_item = tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个角色")
            return
        
        # 获取选中角色的ID
        char_id = tree.item(selected_item[0], 'values')[0]
        # 查找对应的角色
        char = next((c for c in app_state['characters'] if c['id'] == char_id), None)
        if char:
            # 显示角色详情
            detail_text = f"角色ID: {char['id']}\n"
            detail_text += f"游戏名称(game name): {char['game_name']}\n"
            detail_text += f"角色名称(role name): {char['char_name']}\n"
            detail_text += f"角色类型(role type): {char['char_type']}\n"
            
            # 检查是否有图片
            if 'image_path' in char and char['image_path'] and os.path.exists(char['image_path']):
                detail_text += f"图片路径: {char['image_path']}\n"
                
                # 如果PIL可用，显示图片
                if PIL_AVAILABLE:
                    try:
                        # 创建详情对话框
                        detail_window = tk.Toplevel()
                        detail_window.title(f"角色详情(Character Details) - {char['char_name']}")
                        detail_window.geometry("600x400")
                        
                        # 左侧显示角色信息
                        info_frame = ttk.Frame(detail_window)
                        info_frame.pack(side="left", fill="both", expand=True, padx=10, pady=10)
                        
                        info_text = scrolledtext.ScrolledText(info_frame, wrap=tk.WORD, width=30, height=20)
                        info_text.pack(fill="both", expand=True)
                        info_text.insert(tk.END, detail_text)
                        info_text.config(state=tk.DISABLED)  # 设置为只读
                        
                        # 右侧显示图片
                        image_frame = ttk.Frame(detail_window)
                        image_frame.pack(side="right", fill="both", expand=True, padx=10, pady=10)
                        
                        # 加载并调整图片大小
                        image = Image.open(char['image_path'])
                        image.thumbnail((250, 300))  # 调整图片大小以适应窗口
                        photo = ImageTk.PhotoImage(image)
                        
                        # 显示图片
                        image_label = ttk.Label(image_frame, image=photo)
                        image_label.image = photo  # 保持引用以防止被垃圾回收
                        image_label.pack(fill="both", expand=True)
                        
                    except Exception as e:
                        messagebox.showerror("错误", f"无法显示图片: {str(e)}")
                        # 显示文本详情
                        messagebox.showinfo("角色详情", detail_text)
                else:
                    # 显示文本详情
                    messagebox.showinfo("角色详情", detail_text)
            else:
                # 显示文本详情
                messagebox.showinfo("角色详情", detail_text)
    
    def handle_edit_character():
        if 'tree' not in locals():
            return
        
        selected_item = tree.selection()
        if not selected_item:
            messagebox.showinfo("提示(tips)", "请先选择一个角色(Statistical data has been exported to:)")
            return
        
        # 获取选中角色的ID
        char_id = tree.item(selected_item[0], 'values')[0]
        # 查找对应的角色
        char = next((c for c in app_state['characters'] if c['id'] == char_id), None)
        if char:
            def on_edit_success(game_name, char_name, char_type, image_path=None):
                global app_state
                updated_characters, success, message = edit_character(
                    app_state['characters'], char_id, game_name, char_name, char_type, image_path
                )
                if success:
                    # 更新角色数据并刷新UI
                    app_state['characters'] = updated_characters
                    if 'tree' in locals():
                        # 重置为第一页并重新显示
                        app_state['current_page'] = 1
                        if 'pagination_frame' in locals() and pagination_frame:
                            pagination_frame.current_page_var.set("1")
                        display_paged_results(tree)
                    if 'status_var' in locals():
                        status_var.set(f"就绪(ready)，共 {len(app_state['characters'])} 个角色")
                    messagebox.showinfo("成功", message)
                else:
                    messagebox.showerror("错误", message)
            
            # 创建编辑对话框，添加window作为parent参数
            create_edit_character_dialog(window, char, on_edit_success)
    
    def handle_delete_character():
        if 'tree' not in locals():
            return
        
        selected_item = tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个角色")
            return
        
        # 获取选中角色的ID
        char_id = tree.item(selected_item[0], 'values')[0]
        # 确认删除
        if messagebox.askyesno("确认删除", f"确定要删除ID为'{char_id}'的角色吗？"):
            new_characters, success, message = delete_character(app_state['characters'], char_id)
            if success:
                # 更新角色数据并刷新UI
                app_state['characters'] = new_characters
                if 'tree' in locals():
                    # 重置为第一页并重新显示
                    app_state['current_page'] = 1
                    if 'pagination_frame' in locals() and pagination_frame:
                        pagination_frame.current_page_var.set("1")
                    display_paged_results(tree)
                if 'status_var' in locals():
                    status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
                messagebox.showinfo("成功", message)
            else:
                messagebox.showerror("错误", message)
    
    def handle_show_statistics():
        show_statistics_dialog(app_state['characters'])
    
    def handle_refresh_list():
        try:
            # 先保存当前数据，避免丢失未保存的更改
            save_characters(app_state['characters'])
            # 然后从文件重新加载数据
            app_state['characters'] = load_characters()
            if 'tree' in locals():
                # 重置为第一页并重新显示
                app_state['current_page'] = 1
                if 'pagination_frame' in locals() and pagination_frame:
                    pagination_frame.current_page_var.set("1")
                display_paged_results(tree)
            if 'status_var' in locals():
                status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
            messagebox.showinfo("成功", "列表已刷新")
        except Exception as e:
            messagebox.showerror("错误", f"刷新失败: {str(e)}")
    
    def handle_save_data():
        try:
            save_characters(app_state['characters'])
            messagebox.showinfo("成功", "数据已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败: {str(e)}")
    
    def handle_exit_app():
        # 确认退出
        if messagebox.askyesno("确认退出", "确定要退出应用程序吗？"):
            # 保存数据
            try:
                save_characters(app_state['characters'])
            except Exception:
                pass  # 忽略保存错误
            # 退出应用
            sys.exit(0)
    
    # 定义启动屏幕关闭后的回调函数
    def close_splash_and_open_main():
        # 尝试加载数据（在启动屏幕期间）
        try:
            app_state['characters'] = load_characters()
        except Exception as e:
            print(f"加载数据时出错: {str(e)}")
            app_state['characters'] = []
        
        # 显式取消动画定时器
        if hasattr(splash, 'after_id') and splash.after_id:
            splash.after_cancel(splash.after_id)
        
        # 销毁启动屏幕
        splash.destroy()
        root.destroy()  # 销毁隐藏的根窗口
        
        # 创建并显示主窗口
        window = create_main_window()
        
        # 先创建所有UI组件
        # 搜索框架
        global search_frame
        search_frame = create_search_frame(
            window, 
            lambda keyword: handle_search(keyword),
            lambda history: handle_history_select(history),
            lambda page_size, page: handle_page_change(page_size, page)
        )
        
        # 角色列表框架
        tree_frame = ttk.Frame(window)
        tree_frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        global tree
        tree = create_tree_view(tree_frame)
        
        # 初始化UI显示，加载角色数据到tree
        app_state['characters'] = app_state['characters']  # 确保数据一致性
        display_paged_results(tree)
        
        # 分页框架
        total_pages_var = tk.StringVar(value="1/1")
        pagination_frame = create_pagination_frame(
            window,
            lambda page: handle_page_change(app_state['page_size'], page),
            total_pages_var
        )
        
        status_var = create_status_bar(window)
        # 设置状态栏显示角色数量
        status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
        
        # 自动测试搜索功能已禁用，避免NameError
        # window.after(1000, auto_test_search)
        
        # 然后在UI组件创建之后定义回调函数，确保它们能访问所有必要的变量
        def handle_add_character():
            def on_add_success(game_name, char_name, char_type, image_path=None):
                global app_state
                new_characters = create_character(
                    app_state['characters'], game_name, char_name, char_type, image_path
                )
                app_state['characters'] = new_characters
                success = len(new_characters) > len(app_state['characters'])
                message = f"成功添加角色: {char_name} (来自《{game_name}》)"
                # 重置为第一页并刷新显示
                app_state['current_page'] = 1
                if pagination_frame:
                    pagination_frame.current_page_var.set("1")
                display_paged_results(tree)
                status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
                messagebox.showinfo("成功", message)
            
            create_add_character_dialog(window, on_add_success)
        
        def handle_edit_character():
            selected_item = tree.selection()
            if not selected_item:
                messagebox.showinfo("提示", "请先选择一个角色")
                return
            
            # 获取选中角色的ID
            char_id = tree.item(selected_item[0], 'values')[0]
            # 查找对应的角色
            char = next((c for c in app_state['characters'] if c['id'] == char_id), None)
            if char:
                def on_edit_success(game_name, char_name, char_type, image_path=None):
                    global app_state
                    updated_characters, success, message = edit_character(
                        app_state['characters'], char_id, game_name, char_name, char_type, image_path
                    )
                    if success:
                        app_state['characters'] = updated_characters
                        # 重置为第一页并刷新显示
                        app_state['current_page'] = 1
                        if 'pagination_frame' in locals() and pagination_frame:
                            pagination_frame.current_page_var.set("1")
                        display_paged_results(tree)
                        status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
                        messagebox.showinfo("成功", message)
                    else:
                        messagebox.showerror("错误", message)
                
                create_edit_character_dialog(window, char, on_edit_success)
        
        def handle_view_details():
            selected_item = tree.selection()
            if not selected_item:
                messagebox.showinfo("提示", "请先选择一个角色")
                return
            
            # 获取选中角色的ID
            char_id = tree.item(selected_item[0], 'values')[0]
            # 查找对应的角色
            char = next((c for c in app_state['characters'] if c['id'] == char_id), None)
            if char:
                # 显示角色详情
                detail_text = f"角色ID: {char['id']}\n"
                detail_text += f"游戏名称(game name): {char['game_name']}\n"
                detail_text += f"角色名称(role name): {char['char_name']}\n"
                detail_text += f"角色类型(role type): {char['char_type']}\n"
                
                # 检查是否有图片
                if 'image_path' in char and char['image_path'] and os.path.exists(char['image_path']):
                    detail_text += f"图片路径(Image path): {char['image_path']}\n"
                    
                    # 如果PIL可用，显示图片
                    if PIL_AVAILABLE:
                        try:
                            # 创建详情对话框
                            detail_window = tk.Toplevel(window)
                            detail_window.title(f"角色详情(Character Details) - {char['char_name']}")
                            detail_window.geometry("600x400")
                            
                            # 左侧显示角色信息
                            info_frame = ttk.Frame(detail_window)
                            info_frame.pack(side="left", fill="both", expand=True, padx=10, pady=10)
                            
                            info_text = scrolledtext.ScrolledText(info_frame, wrap=tk.WORD, width=30, height=20)
                            info_text.pack(fill="both", expand=True)
                            info_text.insert(tk.END, detail_text)
                            info_text.config(state=tk.DISABLED)  # 设置为只读
                            
                            # 右侧显示图片
                            image_frame = ttk.Frame(detail_window)
                            image_frame.pack(side="right", fill="both", expand=True, padx=10, pady=10)
                            
                            # 加载并调整图片大小
                            image = Image.open(char['image_path'])
                            image.thumbnail((250, 300))  # 调整图片大小以适应窗口
                            photo = ImageTk.PhotoImage(image)
                            
                            # 显示图片
                            image_label = ttk.Label(image_frame, image=photo)
                            image_label.image = photo  # 保持引用以防止被垃圾回收
                            image_label.pack(fill="both", expand=True)
                            
                        except Exception as e:
                            messagebox.showerror("错误", f"无法显示图片: {str(e)}")
                            # 显示文本详情
                            messagebox.showinfo("角色详情", detail_text)
                    else:
                        # 显示文本详情
                        messagebox.showinfo("角色详情", detail_text)
                else:
                    # 显示文本详情
                    messagebox.showinfo("角色详情", detail_text)
            else:
                messagebox.showerror("错误", f"找不到ID为'{char_id}'的角色")
        
        def handle_delete_character():
            selected_item = tree.selection()
            if not selected_item:
                messagebox.showinfo("提示", "请先选择一个角色")
                return
            
            # 获取选中角色的ID
            char_id = tree.item(selected_item[0], 'values')[0]
            # 确认删除
            if messagebox.askyesno("确认删除", f"确定要删除ID为'{char_id}'的角色吗？"):
                new_characters = delete_character(app_state['characters'], char_id)
                success = len(new_characters) < len(app_state['characters'])
                message = "角色删除成功" if success else f"找不到ID为'{char_id}'的角色"
                app_state['characters'] = new_characters
                # 重置为第一页并刷新显示
                app_state['current_page'] = 1
                if 'pagination_frame' in locals() and pagination_frame:
                    pagination_frame.current_page_var.set("1")
                display_paged_results(tree)
                status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
                messagebox.showinfo("成功", message)
        
        def handle_show_statistics():
            show_statistics_dialog(window, app_state['characters'])
        
        def handle_refresh_list():
            try:
                # 先保存当前数据，避免丢失未保存的更改
                save_characters(app_state['characters'])
                # 然后从文件重新加载数据
                app_state['characters'] = load_characters()
                # 重置为第一页并刷新显示
                app_state['current_page'] = 1
                if 'pagination_frame' in locals() and pagination_frame:
                    pagination_frame.current_page_var.set("1")
                display_paged_results(tree)
                status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
                messagebox.showinfo("成功", "列表已刷新")
            except Exception as e:
                messagebox.showerror("错误", f"刷新失败: {str(e)}")
        
        def handle_save_data():
            try:
                save_characters(app_state['characters'])
                messagebox.showinfo("成功", "数据已保存")
            except Exception as e:
                messagebox.showerror("错误", f"保存失败: {str(e)}")
        
        def handle_exit_app():
            # 确认退出
            if messagebox.askyesno("确认退出", "确定要退出应用程序吗？"):
                # 保存数据
                try:
                    save_characters(app_state['characters'])
                except Exception:
                    pass  # 忽略保存错误
                # 退出应用
                window.destroy()
        
        # 创建按钮框架
        callbacks = {
            'add_char': handle_add_character,
            'view_details': handle_view_details,
            'delete_char': handle_delete_character,
            'show_statistics': handle_show_statistics,
            'refresh_list': handle_refresh_list,
            'save_data': handle_save_data,
            'exit_app': handle_exit_app,
            'edit_char': handle_edit_character
        }
        
        create_button_frame(window, callbacks)
        
        # 初始化数据显示
        try:
            display_paged_results(tree)
            status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
            
            # 在主窗口显示后自动测试搜索功能
            # auto_test_search()
        except Exception as e:
            messagebox.showerror("错误", f"初始化失败: {str(e)}")
            status_var.set("初始化失败")
        
        # 运行主循环
        window.protocol("WM_DELETE_WINDOW", handle_exit_app)  # 处理窗口关闭事件
        window.mainloop()
    
    # 设置启动屏幕显示2秒后自动关闭并打开主窗口
    splash.after(2000, close_splash_and_open_main)
    
    # 启动主循环以显示启动屏幕
    splash.mainloop()
    
    # 回调函数
    def handle_add_character():
        def on_add_success(game_name, char_name, char_type, image_path=None):
            global app_state
            new_characters = create_character(
                app_state['characters'], game_name, char_name, char_type, image_path
            )
            app_state['characters'] = new_characters
            success = len(new_characters) > len(app_state['characters'])
            message = f"成功添加角色: {char_name} (来自《{game_name}》)"
            if 'tree' in locals():
                # 重置为第一页并刷新显示
                app_state['current_page'] = 1
                if 'pagination_frame' in locals() and pagination_frame:
                    pagination_frame.current_page_var.set("1")
                display_paged_results(tree)
            if 'status_var' in locals():
                status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
            messagebox.showinfo("成功", message)
        
        create_add_character_dialog(window, on_add_success)
    
    def handle_view_details():
        if 'tree' not in locals():
            return
        
        selected_item = tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个角色")
            return
        
        # 获取选中角色的ID
        char_id = tree.item(selected_item[0], 'values')[0]
        # 查找对应的角色
        char = next((c for c in app_state['characters'] if c['id'] == char_id), None)
        if char:
            # 显示角色详情
            detail_text = f"角色ID: {char['id']}\n"
            detail_text += f"游戏名称: {char['game_name']}\n"
            detail_text += f"角色名称: {char['char_name']}\n"
            detail_text += f"角色类型: {char['char_type']}\n"
            
            # 检查是否有图片
            if 'image_path' in char and char['image_path'] and os.path.exists(char['image_path']):
                detail_text += f"图片路径: {char['image_path']}\n"
                
                # 如果PIL可用，显示图片
                if PIL_AVAILABLE:
                    try:
                        # 创建详情对话框
                        detail_window = tk.Toplevel()
                        detail_window.title(f"角色详情 - {char['char_name']}")
                        detail_window.geometry("600x400")
                        
                        # 左侧显示角色信息
                        info_frame = ttk.Frame(detail_window)
                        info_frame.pack(side="left", fill="both", expand=True, padx=10, pady=10)
                        
                        info_text = scrolledtext.ScrolledText(info_frame, wrap=tk.WORD, width=30, height=20)
                        info_text.pack(fill="both", expand=True)
                        info_text.insert(tk.END, detail_text)
                        info_text.config(state=tk.DISABLED)  # 设置为只读
                        
                        # 右侧显示图片
                        image_frame = ttk.Frame(detail_window)
                        image_frame.pack(side="right", fill="both", expand=True, padx=10, pady=10)
                        
                        # 加载并调整图片大小
                        image = Image.open(char['image_path'])
                        image.thumbnail((250, 300))  # 调整图片大小以适应窗口
                        photo = ImageTk.PhotoImage(image)
                        
                        # 显示图片
                        image_label = ttk.Label(image_frame, image=photo)
                        image_label.image = photo  # 保持引用以防止被垃圾回收
                        image_label.pack(fill="both", expand=True)
                        
                    except Exception as e:
                        messagebox.showerror("错误", f"无法显示图片: {str(e)}")
                        # 显示文本详情
                        messagebox.showinfo("角色详情", detail_text)
                else:
                    # 显示文本详情
                    messagebox.showinfo("角色详情", detail_text)
            else:
                # 显示文本详情
                messagebox.showinfo("角色详情", detail_text)
    
    def handle_edit_character():
        if 'tree' not in locals():
            return
        
        selected_item = tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个角色")
            return
        
        # 获取选中角色的ID
        char_id = tree.item(selected_item[0], 'values')[0]
        # 查找对应的角色
        char = next((c for c in app_state['characters'] if c['id'] == char_id), None)
        if char:
            def on_edit_success(game_name, char_name, char_type, image_path=None):
                global app_state
                updated_characters, success, message = edit_character(
                    app_state['characters'], char_id, game_name, char_name, char_type, image_path
                )
                if success:
                    app_state['characters'] = updated_characters
                    if 'tree' in locals():
                        update_ui_with_characters(tree, app_state['characters'])
                    if 'status_var' in locals():
                        status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
                    messagebox.showinfo("成功", message)
                else:
                    messagebox.showerror("错误", message)
            
            # 创建编辑对话框，添加window作为parent参数
            create_edit_character_dialog(window, char, on_edit_success)
    
    def handle_delete_character():
        if 'tree' not in locals():
            return
        
        selected_item = tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择一个角色")
            return
        
        # 获取选中角色的ID
        char_id = tree.item(selected_item[0], 'values')[0]
        # 确认删除
        if messagebox.askyesno("确认删除", f"确定要删除ID为'{char_id}'的角色吗？"):
            new_characters, success, message = delete_character(app_state['characters'], char_id)
            if success:
                app_state['characters'] = new_characters
                if 'tree' in locals():
                    update_ui_with_characters(tree, app_state['characters'])
                if 'status_var' in locals():
                    status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
                messagebox.showinfo("成功", message)
            else:
                messagebox.showerror("错误", message)
    
    def handle_show_statistics():
        show_statistics_dialog(app_state['characters'])
    
    def handle_refresh_list():
        try:
            # 先保存当前数据，避免丢失未保存的更改
            save_characters(app_state['characters'])
            # 然后从文件重新加载数据
            app_state['characters'] = load_characters()
            if 'tree' in locals():
                update_ui_with_characters(tree, app_state['characters'])
            if 'status_var' in locals():
                status_var.set(f"就绪，共 {len(app_state['characters'])} 个角色")
            messagebox.showinfo("成功", "列表已刷新")
        except Exception as e:
            messagebox.showerror("错误", f"刷新失败: {str(e)}")
    
    def handle_save_data():
        try:
            save_characters(app_state['characters'])
            messagebox.showinfo("成功", "数据已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败: {str(e)}")
    
    def handle_exit_app():
        # 确认退出
        if messagebox.askyesno("确认退出", "确定要退出应用程序吗？"):
            # 保存数据
            try:
                save_characters(app_state['characters'])
            except Exception:
                pass  # 忽略保存错误
            # 退出应用
            sys.exit(0)
    
    # 这些UI组件和回调函数已经在close_splash_and_open_main函数中定义
    # 主循环将在close_splash_and_open_main函数中启动
    
    # 先声明全局变量
    global auto_test_search
    
    # 然后定义自动测试搜索功能
    def auto_test_search():
        # 这个函数将在主窗口显示后自动调用
        print("\n=== 自动测试搜索功能 ===")
        
        # 确保tree和search_frame已经初始化
        print(f"\n测试环境: tree={tree is not None}, search_frame={search_frame is not None}")
        print(f"角色数据: {app_state['characters']}")
        
        # 保存当前状态，以便测试后恢复
        original_is_searching = app_state['is_searching']
        original_search_results = app_state['search_results']
        
        # 执行空搜索测试
        print("\n测试1: 空搜索")
        handle_search("")
        
        # 如果有角色数据，执行更多搜索测试
        if app_state['characters']:
            # 获取第一个角色的信息作为搜索关键词
            first_char = app_state['characters'][0]
            search_key = first_char['char_name']
            print(f"\n测试2: 搜索现有角色关键词 '{search_key}'")
            handle_search(search_key)
            
            # 搜索不存在的关键词
            print("\n测试3: 搜索不存在的关键词 '不存在的角色'")
            handle_search("不存在的角色")
            
            # 测试搜索ID
            if 'id' in first_char:
                id_key = first_char['id']
                print(f"\n测试4: 搜索角色ID '{id_key}'")
                handle_search(id_key)
        else:
            print("\n没有角色数据可供搜索测试")
        
        # 恢复原始状态
        app_state['is_searching'] = original_is_searching
        app_state['search_results'] = original_search_results
        print("\n=== 自动测试搜索功能完成 ===")
        

if __name__ == "__main__":
    run_gui_app()